package com.tianji.promotion.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.UserContext;
import com.tianji.promotion.domain.po.Coupon;
import com.tianji.promotion.domain.po.ExchangeCode;
import com.tianji.promotion.domain.po.UserCoupon;
import com.tianji.promotion.enums.CouponStatus;
import com.tianji.promotion.enums.ExchangeCodeStatus;
import com.tianji.promotion.mapper.CouponMapper;
import com.tianji.promotion.mapper.UserCouponMapper;
import com.tianji.promotion.service.ICouponService;
import com.tianji.promotion.service.IExchangeCodeService;
import com.tianji.promotion.service.IUserCouponService;
import com.tianji.promotion.util.CodeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Objects;

/**
 * <p>
 * 用户领取优惠券的记录，是真正使用的优惠券信息 服务实现类
 * </p>
 *
 * @author xjs
 */
@Service
public class UserCouponServiceImpl extends ServiceImpl<UserCouponMapper, UserCoupon> implements IUserCouponService {

    @Autowired
    @Lazy
    private ICouponService couponService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private IExchangeCodeService codeService;

    @Autowired
    private  CouponMapper couponMapper;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void receiveCoupon(Long couponId) {
        Long userId = UserContext.getUser();
        // 跟据couponId查询coupon
        Coupon coupon = couponService.getById(couponId);
        // 查不到 抛异常
        if (coupon == null) {
            throw new BizIllegalException("查询不到coupon信息：" + couponId);
        }
        // 判断状态是否是发行中
        Integer status = coupon.getStatus();
        if (!Objects.equals(CouponStatus.ISSUING.getValue(), status)) {
            throw new BizIllegalException("coupon状态不对，couponId=" + couponId + ",status:" + status);
        }
        // 判断库存是否充足
        if (coupon.getTotalNum() <= coupon.getIssueNum()) {
            throw new BizIllegalException("coupon库存不足：" + couponId);
        }
        // 判断是否超出限领数量
        int userCouponCount = getUserCouponCount(userId, couponId);
        if (userCouponCount >= coupon.getUserLimit()) {
            throw new BizIllegalException("超过限领数量");
        }
        // coupon的已发放数量+1
        // update coupon set issue_num = issue_num + 1, version = version + 1 where id = #{id} and version = #{version} 会导致有库存也卖不出去
        // update coupon set issue_num = issue_num + 1 where id = #{id} and total_num > issue_num
        int updateResult = couponService.incrementIssuneNum(couponId);
        if (updateResult <= 0) {
            throw new BizIllegalException("超过限领数量");
        }
        // 保存user_coupon
        UserCoupon uc = new UserCoupon();
        uc.setUserId(userId);
        uc.setCouponId(couponId);
        LocalDateTime begin = coupon.getTermBeginTime();
        LocalDateTime end = coupon.getTermEndTime();
        if (begin == null) {
            // 说明优惠券的有效期是从领用开始，termDays之内有效
            begin = LocalDateTime.now();
            end = LocalDateTime.now().plusDays(coupon.getTermDays());
        }
        uc.setTermBeginTime(begin);
        uc.setTermEndTime(end);
        uc.setStatus(1);
        this.save(uc);
    }

    @Override
    @Transactional
    public void exchangeCoupon(String code) {
        // 1.校验并解析兑换码
        long serialNum = CodeUtil.parseCode(code);
        // 2.校验是否已经兑换 SETBIT KEY 4 1 ，这里直接执行setbit，通过返回值来判断是否兑换过
        boolean exchanged = codeService.updateExchangeMark(serialNum, true);
        if (exchanged) {
            throw new BizIllegalException("兑换码已经被兑换过了");
        }
        try {
            // 3.查询兑换码对应的优惠券id
            ExchangeCode exchangeCode = codeService.getById(serialNum);
            if (exchangeCode == null) {
                throw new BizIllegalException("兑换码不存在！");
            }
            // 4.是否过期
            LocalDateTime now = LocalDateTime.now();
            if (now.isAfter(exchangeCode.getExpiredTime())){
                throw new BizIllegalException("兑换码已经过期");
            }
            // 5.校验并生成用户券
            // 5.1.查询优惠券
            Coupon coupon = couponMapper.selectById(exchangeCode.getExchangeTargetId());
            // 5.2.查询用户
            Long userId = UserContext.getUser();
            // 5.3.校验并生成用户券，更新兑换码状态
            checkAndCreateUserCoupon(coupon, userId, (int)serialNum);
        } catch (Exception e) {
            // 重置兑换的标记 0
            codeService.updateExchangeMark(serialNum, false);
            throw e;
        }
    }

    private void checkAndCreateUserCoupon(Coupon coupon, Long userId, Integer serialNum){
        // 1.校验每人限领数量
        // 1.1.统计当前用户对当前优惠券的已经领取的数量
        Integer count = lambdaQuery()
                .eq(UserCoupon::getUserId, userId)
                .eq(UserCoupon::getCouponId, coupon.getId())
                .count();
        // 1.2.校验限领数量
        if(count != null && count >= coupon.getUserLimit()){
            throw new BadRequestException("超出领取数量");
        }
        // 2.更新优惠券的已经发放的数量 + 1
        couponMapper.incrementIssuneNum(coupon.getId());
        // 3.新增一个用户券
        saveUserCoupon(coupon, userId);
        // 4.更新兑换码状态
        if (serialNum != null) {
            codeService.lambdaUpdate()
                    .set(ExchangeCode::getUserId, userId)
                    .set(ExchangeCode::getStatus, ExchangeCodeStatus.USED)
                    .eq(ExchangeCode::getId, serialNum)
                    .update();
        }
    }

    private void saveUserCoupon(Coupon coupon, Long userId) {
        // 1.基本信息
        UserCoupon uc = new UserCoupon();
        uc.setUserId(userId);
        uc.setCouponId(coupon.getId());
        // 2.有效期信息
        LocalDateTime termBeginTime = coupon.getTermBeginTime();
        LocalDateTime termEndTime = coupon.getTermEndTime();
        if (termBeginTime == null) {
            termBeginTime = LocalDateTime.now();
            termEndTime = termBeginTime.plusDays(coupon.getTermDays());
        }
        uc.setTermBeginTime(termBeginTime);
        uc.setTermEndTime(termEndTime);
        // 3.保存
        save(uc);
    }


    private int getUserCouponCount(Long userId, Long couponId) {
        return this.lambdaQuery()
                .eq(UserCoupon::getUserId, userId)
                .eq(UserCoupon::getCouponId, couponId)
                .count();
    }
}
